package com.example.demo.sharding.filter;

import com.example.demo.basic.DataBaseContextHolder;
import com.example.demo.config.DataBaseHolder;
import com.example.demo.entity.AutoDatabaseRouteByBookNum;
import com.example.demo.mapper.AutoDatabaseRouteByBookNumMapper;
import com.example.demo.sharding.RouteModel;
import com.example.demo.sharding.annotation.NeedRoute;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 类描述:@NeedRoute自定义注解的业务处理，
 * 切换数据源的自动切面；
 *
 * @author zhanglei
 * @date 2019/3/8 14:10
 **/

@Component
@Aspect
@Order(0)
@Slf4j
public class NodeRouteInterceptor {

    @Autowired
    AutoDatabaseRouteByBookNumMapper autoDatabaseRouteByBookNumMapper;

    @Autowired
    DataBaseHolder dataBaseHolder;

    @Pointcut("@annotation(com.example.demo.sharding.annotation.NeedRoute)")
    public void aopPoint() {
        System.out.println("aop...");
    }

    @After("aopPoint()")
    public void afterRoute() {
        DataBaseContextHolder.clear();
    }

    @Before("aopPoint()")
    public void doRoute(JoinPoint jp) throws Exception {

        Object target = jp.getTarget();
        Signature signature = jp.getSignature();
        if (!(signature instanceof MethodSignature)) {
            throw new Exception("请求路径异常，signature instanceof MethodSignature = false");
        }
        MethodSignature ms = (MethodSignature) signature;
        Method method = target.getClass().getMethod(signature.getName(), ms.getParameterTypes());
        NeedRoute annotation = method.getAnnotation(NeedRoute.class);
        if (annotation == null) {
            throw new Exception("切面异常，未发现指定注解，annotation: " + annotation);
        }

        Object[] args = jp.getArgs();

        if (args == null || args.length < 1) {
            throw new Exception("路由方法参数不正确");
        }
        Object firstArg = args[0];
        if ((firstArg instanceof RouteModel) == false) {
            throw new Exception("路由方法参数类型不正确");
        }
        RouteModel routeModel = (RouteModel) firstArg;

        dynamicRoute(routeModel);

        log.debug("routeInterceptor, firstArg: {} , targetTableID : {} ", routeModel, routeModel.getTableID());
    }


    private void dynamicRoute(RouteModel routeModel) throws Exception {

        // 动态路由已存在时，不重复获取
        if (routeModel.getDbID() != null && routeModel.getTableName() != null) {
            return;
        }
        if (routeModel.getDbID() != null && routeModel.getTableID() != null) {
            routeModel.setTableName(routeModel.getTablePrefix() + "_" + routeModel.getTableID());
            return;
        }

        //根据num查询所在数据库和表
        String num = routeModel.getNum();
        AutoDatabaseRouteByBookNum routeInfo = autoDatabaseRouteByBookNumMapper.selectByNum(num);
        if (routeInfo == null) {
            log.info("没有找到该编号的路由信息:" + num);
            DataBaseContextHolder.clear();
            throw new Exception("企业资质未配置完全,没有找到该编号的路由信息:" + num);
        } else {

            //判断数据源是否存在，如果不存在，直接抛出异常；
            if (dataBaseHolder.existDatabase(routeInfo.getDbId()) == false) {
                throw new Exception("没有找到该数据源，dbID = " + routeInfo.getDbId());
            }

            DataBaseContextHolder.setDBID(routeInfo.getDbId());
            DataBaseContextHolder.setTableID(routeInfo.getTableId());
            routeModel.setTableID(routeInfo.getTableId());
            routeModel.setTableName(routeModel.getTablePrefix() + "_" + routeInfo.getTableId());
        }
    }
}
